from datetime import datetime

from decimal import Decimal

from database import db_session

from flask import session

from models import *

from sqlalchemy import exc
from sqlalchemy import or_
from sqlalchemy import func

import tipositem_mgr

def buscar_items(cadena, campo):
    """Realiza la busqueda de los items de acuerdo al filtro 
    seleccionado (nombre, descripcion, observacion, estado, costo,
    complejidad) y devuelve las conincidencias encontradas
    @param cadena: indica el dato que se quiere buscar
    @param campo: indica por cual campo se va a buscar
    @return items: retorna la lista con las coincidencias"""
    nombre_proyecto = session["nombre_proyecto"]
    nombre_fase = session["nombre_fase"]

    lista_items = []
    costo = None

    proyecto = db_session.query(Proyecto).filter_by\
    (nombre=nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(fk_proyecto=proyecto.id).\
    filter_by(nombre=nombre_fase).one()

    tipos_item = db_session.query(TipoItem).filter_by(fk_fase=fase.id).\
    all()

    if campo == "todos":

        if cadena.isdecimal() == True:
            costo = Decimal(cadena)

        try:
            complejidad=int(cadena)
        except:
            complejidad=None

        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by(\
            fk_tipo_item=tipoitem.id).filter(or_(\
            func.lower(Item.nombre).contains(cadena),\
            func.lower(Item.descripcion).contains(cadena),\
            func.lower(Item.observacion).contains(cadena),\
            func.lower(Item.estado).contains(cadena),\
            Item.complejidad==complejidad,\
            Item.costo==costo)).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "nombre":
        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by\
            (fk_tipo_item=tipoitem.id).filter\
            (func.lower(Item.nombre).contains(cadena)).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "descripcion":
        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by\
            (fk_tipo_item=tipoitem.id).filter\
            (func.lower(Item.descripcion).contains(cadena)).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "observacion":
        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by\
            (fk_tipo_item=tipoitem.id).filter\
            (func.lower(Item.observacion).contains(cadena)).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "estado":
        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by\
            (fk_tipo_item=tipoitem.id).filter\
            (func.lower(Item.estado).contains(cadena)).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "complejidad":

        try:
            complejidad=int(cadena)
        except:
            complejidad=None

        for tipoitem in tipos_item:
            item = db_session.query(Item).filter_by\
            (fk_tipo_item=tipoitem.id).filter\
            (Item.complejidad==complejidad).all()

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "costo":

        for tipoitem in tipos_item:
            if verificar_valor_costo(cadena):
                costo = Decimal(cadena)
                item = db_session.query(Item).filter_by\
                (fk_tipo_item=tipoitem.id, costo=costo).all()
            else: #El valor no es un numero valido
                return lista_items
                #Se retorna una lista vacia

            for elemento_item in item:
                if elemento_item.estado != 'INA':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=tipoitem.nombre)

                    lista_items.append(lista_item)

    if campo == "tipo_item":
        lista = tipositem_mgr.buscar_tipoitem(cadena, 'nombre')

        for elem in lista:
            items = db_session.query(Item).filter_by\
            (fk_tipo_item=elem['id']).all()

            for elemento_item in items:
                if elemento_item.estado != 'INA' and \
                elemento_item.estado != 'ELI':
                    lista_item = dict(nombre=elemento_item.nombre,\
                    descripcion=elemento_item.descripcion, \
                    observacion=elemento_item.observacion, \
                    estado=elemento_item.estado, \
                    complejidad=elemento_item.complejidad, \
                    costo=elemento_item.costo, tipo_item=elem['nombre'])

                    lista_items.append(lista_item)

    lista_items = sorted(lista_items, key=lambda \
    elemento: elemento['nombre'])
    return lista_items

def verificar_valor_costo(costo):
    """Verifica que el costo ingresado sea numericamente 
    valido
    @param costo: Valor del costo del item"""
    
    try:
        resultado = Decimal(costo)
    except:
        return False

    return True

def listar_tipoitem_crear_item(nombre_proyecto, nombre_fase):
    """Lista los tipos de item de la fase actual, para presentar las 
    opciones en el select de la pagina de crear item
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual"""
    items = []

    proyecto = db_session.query(Proyecto).filter_by\
    (nombre=nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by\
    (nombre=nombre_fase, fk_proyecto=proyecto.id).one()

    for tipoItem in fase.tipos_item:
        item = dict(id=tipoItem.id, nombre=tipoItem.nombre)
        items.append(item)

    items = sorted(items, key=lambda elemento: elemento['nombre'])
    return items

def traer_atributos_tipoitem(id_tipo_item):
    """Recupera los atributos del tipo de item seleccionado
    @param id_tipo_item: Id del tipo de item seleccionado"""
    items = []
    atributos = db_session.query(Atributo).filter_by(fk_tipo_item=\
    id_tipo_item).all()

    for atributo in atributos:
        if atributo.tipo != 4:
            nombre = atributo.nombre

            if atributo.tipo == 1: #Numerico
                nombre = nombre + " [NUM]"
                if atributo.precision != None and \
                atributo.precision != '':
                    nombre = nombre + " [" + str(atributo.precision) + "]"
            if atributo.tipo == 2: #Cadena
                nombre = nombre + " [CAD]"
                if atributo.longitud != None and \
                atributo.longitud != '':
                    nombre = nombre + " [" + str(atributo.longitud) + "]"
            if atributo.tipo == 3: #Fecha
                nombre = nombre + " [FEC]"

            item = dict(id=atributo.id, nombre=nombre, valor="", \
            obligatorio=atributo.obligatorio, tipo=atributo.tipo)
            items.append(item)
        else:
            #Esto se hace para evitar que se incluyan atributos binarios
            pass

    items = sorted(items, key=lambda elemento: elemento['id'])
    return items

def validaciones_boton_crear_item(nombre_proyecto, nombre_fase):
    """Validaciones para crear item
    @param nombre_proyecto: NOmbre del proyecto
    @param nombe_fase: Nombre de la fase"""
    error = None
    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    if fase.estado != 'PEN' and fase.estado != 'ACT':
        error = 'No puede crear items nuevos en esta fase'
        return error

    return error

def validaciones_parametros_item(id_tipo_item_numerico, \
    nombre_proyecto, nombre_fase, nombre, descripcion, complejidad, \
    costo, lista_atributos):
    """Valida que los valores del nuevo item cumplan con las condiciones
    requeridas por el negocio
    @param id_tipo_item_numerico: Id del tipo de item seleccionado
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual
    @param nombre: Nombre ingresado para el nuevo item
    @param descripcion: Descripcion ingresada para el nuevo item
    @param complejidad: Complejidad ingresada para el nuevo item
    @param costo: Costo ingresado para el nuevo item
    @param lista_atributos: Lista de los atributos y valores del nuevo item"""
    error = None
    if nombre == "":
        error = "Debe ingresar un nombre de item"
        return error
    if descripcion == "":
        error = "Debe ingresar una descripcion de item"
        return error
    if complejidad == "":
        error = "Debe ingresar la complejidad de item"
        return error
    if costo == "":
        error = "Debe ingresar el costo de item"
        return error
    if id_tipo_item_numerico == 0:
        error = "Debe seleccionar el tipo de item"
        return error

    #Se valida que la complejidad sea un valor entero
    try:
        complejidad_entero = int(complejidad)
    except:
        error = "El valor de la complejidad del item no es correcto. \
        Debe ser un valor entero"
        return error

    #Se valida que el costo sea un valor numerico
    try:
        costo_decimal = Decimal(costo)
    except:
        error = "El valor del costo del item no es correcto. Debe ser \
        un valor decimal"
        return error

    #Se valida que el nombre del item sea unico en la fase
    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    tipos_item = db_session.query(TipoItem).filter_by(fk_fase=\
    fase.id).all()

    for tipo in tipos_item:
        items = db_session.query(Item).filter_by(fk_tipo_item=\
        tipo.id).all()
        for item in items:
            if nombre == item.nombre and item.estado != 'INA' \
            and item.estado != 'ELI':
                error = "El nombre del item debe ser unico en la fase"
                return error

    atributos = db_session.query(Atributo).filter_by(fk_tipo_item=\
    id_tipo_item_numerico).all()

    #Se valida que los atributos obligatorios hayan sido ingresados
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.obligatorio == "S" and \
                elemento['valor'] == "":
                    error = "Atributo '" + atributo.nombre + "' no ingresado"
                    return error

    #Se valida que los datos de tipo numerico sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 1:
                    try:
                        valor = elemento['valor']
                        if valor != '':
                            resultado = Decimal(valor)
                        else:
                            break
                    except:
                        error = "El valor del atributo '" + \
                        atributo.nombre + "' no es valido. Debe ser de \
                        tipo Numerico"
                        return error

                    #Se valida la precision
                    if atributo.precision != None and \
                    atributo.precision != '':
                        valor = elemento['valor']
                        try:
                            entero, decimal = valor.split('.')
                        except:
                            decimal = ''
                        if len(decimal) > atributo.precision:
                            error = "La precision del atributo '" + \
                            atributo.nombre + "' no es valida. Debe \
                            ser " + str(atributo.precision)
                            return error

    #Se valida que los datos de tipo cadena sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 2:
                    #Se valida la longitud
                    if atributo.longitud != None and \
                    atributo.longitud != '':
                        valor = elemento['valor']

                        if len(valor) > atributo.longitud:
                            error = "La longitud del atributo '" + \
                            atributo.nombre + "' no es valida. Debe \
                            ser como maximo " + str(atributo.longitud)
                            return error

    #Se valida que los datos de tipo fecha sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 3:
                    try:
                        valor = elemento['valor']
                        if valor != '':
                            day, month, year = map(int, valor.split('/'))
                            date_object = datetime(year, month, day)
                            break
                        else:
                            break
                    except:
                        error = "El valor del atributo '" + \
                        atributo.nombre + "' no es valido. Debe ser de \
                        tipo Fecha [dd/mm/aaaa]"
                        return error

    return error

def cargar_valores_atributos(atributos, lista_atributos):
    """Arma la lista de atributos con sus valores correspondientes
    @param atributos: lista de atributos con sus elementos
    @param lista_atributos: lista de id de atributo con su valor"""
    items = []
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo['id'] == elemento['pk']:
                if atributo['tipo'] == 1:
                    try:
                        aux = str(elemento['valor'])
                        vl = Decimal(aux).normalize()
                    except:
                        vl = elemento['valor']
                else:
                    vl = elemento['valor']

                valor = str(vl)

                item = dict(id=atributo['id'], nombre=\
                atributo['nombre'], valor=valor, \
                obligatorio=atributo['obligatorio'], \
                tipo=atributo['tipo'])
                items.append(item)
                break

    items = sorted(items, key=lambda elemento: elemento['id'])
    return items

def insertar_item(id_tipo_item_numerico, nombre_proyecto, nombre_fase, \
    id_usuario, nombre, descripcion, observacion, complejidad, costo, \
    lista_atributos):
    """Inserta el nuevo item en la base de datos
    @param id_tipo_item_numerico: Id del tipo de item seleccionado
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual
    @param id_usuario: Id del usuario creador
    @param nombre: Nombre del nuevo item
    @param descripcion: Descripcion del nuevo item
    @param observacion: Observacion del nuevo item
    @param complejidad: Complejidad del nuevo item
    @param costo: Costo del nuevo item
    @param lista_atributos: Lista de atributos del nuevo item"""
    error = None

    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    fecha_actual = datetime.now()
    usuario_creador = usuario.id
    complejidad_entero = int(complejidad)
    costo_decimal = Decimal(costo)
    try:
        if fase.estado == 'PEN':
            fase.estado = 'ACT'
            fase.fecha_modificacion = fecha_actual
            fase.usuario_modificacion = usuario_creador

            db_session.add(fase)
            db_session.flush()

        item = Item()

        item.nombre = nombre
        item.descripcion = descripcion
        item.observacion = observacion
        item.estado = 'ACT'
        item.version = 1
        item.complejidad = complejidad_entero
        item.costo = costo_decimal
        item.fk_tipo_item = id_tipo_item_numerico
        item.fecha_creacion = fecha_actual
        item.usuario_creacion = usuario_creador
        item.fecha_modificacion = fecha_actual
        item.usuario_modificacion = usuario_creador

        db_session.add(item)
        db_session.flush()

        for atributo in lista_atributos:
            id_atributo = atributo['pk']
            valor_atributo = atributo['valor']

            atrib = db_session.query(Atributo).filter_by(id=\
            id_atributo).one()

            valor = Valor()

            valor.fk_item = item.id
            valor.fk_atributo = atrib.id
            if atrib.tipo == 1 and valor_atributo != '':
                vl = str(valor_atributo)
                valor.valor_numerico = Decimal(vl)
            if atrib.tipo == 2 and valor_atributo != '':
                valor.valor_cadena = valor_atributo
            if atrib.tipo == 3 and valor_atributo != '':
                day, month, year = map(int, valor_atributo.split('/'))
                date_object = datetime(year, month, day)
                valor.valor_fecha = date_object

            db_session.add(valor)
            db_session.flush()

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def verificar_parametros_modificar_item(seleccionados):
    """Valida que se seleccione un item para modificar, y solo uno.
    Ademas valida que el item no se encuentre ni en revision ni
    aprobado
    @param seleccionados: Lista de id de items seleccionados"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un item
        error = "Debe seleccionar un unico item"
        return error

    #El item a modificar debe estar en estado Activo
    id_item = int(seleccionados[0])
    item = db_session.query(Item).filter_by(id=id_item).one()

    if item.estado == 'REV':
        error = "Se requiere un cambio de estado a Activo"
        return error

    if item.estado == 'APR':
        error = "Se requiere un cambio de estado a Activo"
        return error

    if item.estado == 'BLO':
        error = "No se puede modificar un item Bloqueado"
        return error

    id_usuario = session["id_usuario"]
    duenho = usuario_dueno(id_item)

    if duenho != None and duenho != id_usuario:
        error = "No esta autorizado a modificar el Item"
        return error

    return error

def obtener_id_tipo_item(id_item):
    """Recupera el id del tipo de item del item dado
    @param id_item: Id del item seleccionado"""
    item = db_session.query(Item).filter_by(id=id_item).one()
    id_tipo_item = item.fk_tipo_item
    return id_tipo_item

def obtener_nombre_tipo_item(id_tipo_item):
    """Recupera el nombre del tipo de item del item dado
    @param id_tipo_item: Id del tipo de item seleccionado"""
    tipo_item = db_session.query(TipoItem).filter_by(id=\
    id_tipo_item).one()
    nombre_tipo_item = tipo_item.nombre
    return nombre_tipo_item

def validaciones_parametros_modificar_item(id_tipo_item_numerico, \
    nombre_proyecto, nombre_fase, nombre, descripcion, complejidad, \
    costo, lista_atributos, id_item_numerico):
    """Valida que los valores del item a modificar cumplan con las 
    condiciones requeridas por el negocio
    @param id_tipo_item_numerico: Id del tipo de item del item dado
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual
    @param nombre: Nombre del item a modificar
    @param descripcion: Descripcion del item a modificar
    @param complejidad: Complejidad del item a modificar
    @param costo: Costo del item a modificar
    @param lista_atributos: Lista de los atributos del item a modificar
    @param id_item_numerico: Id del item a modificar"""
    error = None
    if nombre == "":
        error = "Debe ingresar un nombre de item"
        return error
    if descripcion == "":
        error = "Debe ingresar una descripcion de item"
        return error
    if complejidad == "":
        error = "Debe ingresar la complejidad de item"
        return error
    if costo == "":
        error = "Debe ingresar el costo de item"
        return error

    #Se valida que la complejidad sea un valor entero
    try:
        complejidad_entero = int(complejidad)
    except:
        error = "El valor de la complejidad del item no es correcto. \
        Debe ser un valor entero"
        return error

    #Se valida que el costo sea un valor numerico
    try:
        costo_decimal = Decimal(costo)
    except:
        error = "El valor del costo del item no es correcto. Debe ser \
        un valor decimal"
        return error

    #Se valida que el nombre del item sea unico en la fase
    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    tipos_item = db_session.query(TipoItem).filter_by(fk_fase=\
    fase.id).all()

    for tipo in tipos_item:
        items = db_session.query(Item).filter_by(fk_tipo_item=\
        tipo.id).all()
        for item in items:
            if nombre == item.nombre and id_item_numerico != item.id \
            and item.estado != 'INA':
                error = "El nombre del item debe ser unico en la fase"
                return error

    atributos = db_session.query(Atributo).filter_by(fk_tipo_item=\
    id_tipo_item_numerico).all()

    #Se valida que los atributos obligatorios hayan sido ingresados
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.obligatorio == "S" and \
                elemento['valor'] == "":
                    error = "Atributo '" + atributo.nombre + "' no ingresado"
                    return error

    #Se valida que los datos de tipo numerico sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 1:
                    try:
                        valor = elemento['valor']
                        if valor != '':
                            resultado = Decimal(valor)
                        else:
                            break
                    except:
                        error = "El valor del atributo '" + \
                        atributo.nombre + "' no es valido. Debe ser de \
                        tipo Numerico"
                        return error

                    #Se valida la precision
                    if atributo.precision != None and \
                    atributo.precision != '':
                        valor = elemento['valor']
                        try:
                            entero, decimal = valor.split('.')
                        except:
                            decimal = ''
                        if len(decimal) > atributo.precision:
                            error = "La precision del atributo '" + \
                            atributo.nombre + "' no es valida. Debe \
                            ser " + str(atributo.precision)
                            return error

    #Se valida que los datos de tipo cadena sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 2:
                    #Se valida la longitud
                    if atributo.longitud != None and \
                    atributo.longitud != '':
                        valor = elemento['valor']

                        if len(valor) > atributo.longitud:
                            error = "La longitud del atributo '" + \
                            atributo.nombre + "' no es valida. Debe \
                            ser como maximo " + str(atributo.longitud)
                            return error

    #Se valida que los datos de tipo fecha sean validos
    for atributo in atributos:
        for elemento in lista_atributos:
            if atributo.id == elemento['pk']:
                if atributo.tipo == 3:
                    try:
                        valor = elemento['valor']
                        if valor != '':
                            day, month, year = map(int, valor.split('/'))
                            date_object = datetime(year, month, day)
                            break
                        else:
                            break
                    except:
                        error = "El valor del atributo '" + \
                        atributo.nombre + "' no es valido. Debe ser de \
                        tipo Fecha [dd/mm/aaaa]"
                        return error

    #Falta incluir las validaciones de longitud de cadena y precision
    #de numericos

    return error

def obtener_datos_item(id_item):
    """Obtiene los valores del item dado
    @param id_item: Id del item dado"""
    item = db_session.query(Item).filter_by(id=\
    id_item).one()

    elemento = dict(nombre=item.nombre, descripcion=item.descripcion, \
    observacion=item.observacion, complejidad=item.complejidad, \
    costo=item.costo)

    return elemento

def obtener_valor_atributo(id_item, id_atributo):
    """Obtiene el valor asignado al atributo dado
    @param: id_item: Id del item seleccionado
    @param: id_atributo: Id del atributo seleccionado"""
    respuesta = ''

    atributo = db_session.query(Atributo).filter_by(id=\
    id_atributo).one()

    valor = db_session.query(Valor).filter_by(fk_item=id_item,\
    fk_atributo=id_atributo).one()

    if atributo.tipo == 1 and valor.valor_numerico != None:
        respuesta = valor.valor_numerico

    if atributo.tipo == 2 and valor.valor_cadena != None:
        respuesta = valor.valor_cadena

    if atributo.tipo == 3 and valor.valor_fecha != None:
        fecha = valor.valor_fecha
        year = fecha.year
        month = fecha.month
        day = fecha.day
        respuesta = str(day) + "/" + str(month) + "/" + str(year)

    return respuesta

def modificacion_item(id_item, nombre_proyecto, nombre_fase, \
    id_usuario, nombre, descripcion, observacion, complejidad, costo, \
    lista_atributos):
    """Almacena en la base de datos las modificaciones para el item 
    dado. Guarda en el historial la version anterior de sus valores, 
    asi como de sus relaciones
    @param id_item: Id del item a modificar
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual
    @param id_usuario: Id del usuario modificador
    @param nombre: Nombre del item modificado
    @param descripcion: Descripcion del item modificado
    @param observacion: Observacion del item modificado
    @param complejidad: Complejidad del item modificado
    @param costo: Costo del item modificado
    @param lista_atributos: Atributos del item modificado"""
    error = None

    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    fecha_actual = datetime.now()
    usuario_modificador = usuario.id
    complejidad_entero = int(complejidad)
    costo_decimal = Decimal(costo)
    try:
        item = db_session.query(Item).filter_by(id=\
        id_item).one()
        item_nuevo = Item()

        item.estado = 'INA'
        item.fecha_modificacion = fecha_actual
        item.usuario_modificacion = usuario_modificador

        db_session.add(item)
        db_session.flush()

        item_nuevo.nombre = nombre
        item_nuevo.descripcion = descripcion
        item_nuevo.observacion = observacion
        item_nuevo.estado = 'ACT'
        item_nuevo.version = item.version + 1
        item_nuevo.complejidad = complejidad_entero
        item_nuevo.costo = costo_decimal
        item_nuevo.fk_tipo_item = item.fk_tipo_item
        item_nuevo.item_anterior = item.id
        item_nuevo.fecha_creacion = fecha_actual
        item_nuevo.usuario_creacion = usuario_modificador
        item_nuevo.fecha_modificacion = fecha_actual
        item_nuevo.usuario_modificacion = usuario_modificador

        db_session.add(item_nuevo)
        db_session.flush()

        for atributo in lista_atributos:
            id_atributo = atributo['pk']
            valor_atributo = atributo['valor']

            atrib = db_session.query(Atributo).filter_by(id=\
            id_atributo).one()

            valor = Valor()

            valor.fk_item = item_nuevo.id
            valor.fk_atributo = atrib.id
            if atrib.tipo == 1:
                if valor_atributo != '':
                    vl = str(valor_atributo)
                    valor.valor_numerico = Decimal(vl)
                else:
                    valor.valor_numerico = None
            if atrib.tipo == 2:
                valor.valor_cadena = valor_atributo
            if atrib.tipo == 3:
                if valor_atributo != '':
                    day, month, year = map(int, valor_atributo.split('/'))
                    date_object = datetime(year, month, day)
                    valor.valor_fecha = date_object
                else:
                    valor.valor_fecha = None

            db_session.add(valor)
            db_session.flush()

        relaciones = db_session.query(Relacion).filter_by(\
        fk_item_destino=item.id, estado='ACT').all()

        for relacion in relaciones:
            relacion_nueva = Relacion()

            relacion_nueva.fk_item_origen = relacion.fk_item_origen
            relacion_nueva.fk_item_destino = item_nuevo.id
            relacion_nueva.estado = 'ACT'

            db_session.add(relacion_nueva)
            db_session.flush()

            relacion.estado = 'INA'

            db_session.add(relacion)
            db_session.flush()

        relaciones = db_session.query(Relacion).filter_by(\
        fk_item_origen=item.id, estado='ACT').all()

        for relacion in relaciones:
            relacion_nueva = Relacion()

            relacion_nueva.fk_item_origen = item_nuevo.id
            relacion_nueva.fk_item_destino = relacion.fk_item_destino
            relacion_nueva.estado = 'ACT'

            db_session.add(relacion_nueva)
            db_session.flush()

            relacion.estado = 'INA'

            db_session.add(relacion)
            db_session.flush()

        resultado = establecer_estado_revision(item_nuevo.id)
        if resultado != 'OK':
            db_session.rollback()
            return resultado

        resultado = establecer_estado_aprobado()
        if resultado != 'OK':
            db_session.rollback()
            return resultado

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def establecer_estado_revision(id_item):
    """Recorre la lista de hijos y sucesores del item dado, y establece 
    el estado de los mismos en Revision. Ademas establece las lineas 
    base afectadas en estado Comprometida
    @param id_item: Id del item inicio de la lista a recorrer"""
    try:
        relaciones = db_session.query(Relacion).filter_by(\
        fk_item_origen=id_item, estado='ACT').all()

        for relacion in relaciones:
            resultado = establecer_estado_revision(\
            relacion.fk_item_destino)
            if resultado == 'OK':
                item = db_session.query(Item).filter_by(\
                id=relacion.fk_item_destino).one()

                if item.estado != 'INA' and item.estado != 'ELI':
                    item.estado = 'REV'
                    db_session.add(item)
                    db_session.flush()

                    #La linea base afectada pasa a estado comprometida
                    for lb in item.lineabase:
                        if lb.estado == 'ACT':
                            lb.estado = 'COM'
                            db_session.add(lb)
                            db_session.flush()

            else:
                return resultado

        return 'OK'

    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

def establecer_estado_aprobado():
    """Establece en estado Aprobado los items miembros de las lineas 
    base que contiene uno o mas items en Revision"""
    try:
        lineas_base_comprometidas = db_session.query(LineaBase).filter_by(\
        estado='COM').all()

        #Se recorre la lista de lineas base comprometidas
        for lb in lineas_base_comprometidas:
            items = lb.items
            #Se recorre la lista de items miembros de la linea base
            for item in items:
                #Los items Bloqueados pasan a Aprobado
                if item.estado == 'BLO':
                    item.estado = 'APR'

                    db_session.add(item)
                    db_session.flush()

        return 'OK'
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

def listar_items_antecesores(nombre_proyecto, nombre_fase):
    """Devuelve la lista de items candidatos a antecesores en una 
    relacion
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual"""
    lista = []
    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase_actual = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    if fase_actual.orden != 1:
        orden_anterior = fase_actual.orden - 1

        fase_anterior = db_session.query(Fase).filter_by(orden=\
        orden_anterior, fk_proyecto=proyecto.id).one()

        tipos_item = db_session.query(TipoItem).filter_by(fk_fase=\
        fase_anterior.id).all()

        for tipo_item in tipos_item:
            items = db_session.query(Item).filter_by(fk_tipo_item=\
            tipo_item.id).all()

            for item in items:
                if item.estado != 'INA' and item.estado != 'ELI':
                    nombre = fase_anterior.nombre + " -- " + item.nombre
                    elem = dict(id=item.id, nombre=nombre)
                    lista.append(elem)

        lista = sorted(lista, key=lambda elemento: elemento['nombre'])

    return lista

def listar_items_padres(nombre_proyecto, nombre_fase):
    """Devuelve la lista de items candidatos a padres en una 
    relacion
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual"""
    lista = []
    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    fase_actual = db_session.query(Fase).filter_by(nombre=\
    nombre_fase, fk_proyecto=proyecto.id).one()

    tipos_item = db_session.query(TipoItem).filter_by(fk_fase=\
    fase_actual.id).all()

    for tipo_item in tipos_item:
        items = db_session.query(Item).filter_by(fk_tipo_item=\
        tipo_item.id).all()

        for item in items:
            if item.estado != 'INA' and item.estado != 'ELI':
                nombre = fase_actual.nombre + " -- " + item.nombre
                elem = dict(id=item.id, nombre=nombre)
                lista.append(elem)

    lista = sorted(lista, key=lambda elemento: elemento['nombre'])

    return lista

def validar_datos_crear_relacion(item_origen, item_destino):
    """Valida que se seleccione un origen y un destino, y que estos 
    no sean el mismo Item. Ademas previene la formacion de ciclos
    @param item_origen: Id del Item origen
    @param item_destino: Id del Item destino"""
    error = None

    if item_origen == 0:
        error = "Debe seleccionar un origen"
        return error
    if item_destino == 0:
        error = "Debe seleccionar un destino"
        return error
    if item_origen == item_destino:
        error = "El item origen y destino no pueden ser los mismos"
        return error

    ori = int(item_origen)
    des = int(item_destino)

    relacion = db_session.query(Relacion).filter_by(\
    fk_item_origen=ori, fk_item_destino=des).first()

    if relacion != None:
        error = "La relacion ya existe"
        return error

    hayCiclo = verificar_ciclos(des, ori)
    if hayCiclo:
        error = "No deben formarse ciclos. Relacion no valida"
        return error

    return error

def verificar_ciclos(item_origen, item_destino):
    """Recorre la lista de items relacionados, en busca de ciclos
    @param item_origen: Id del Item candidato a origen
    @param item_destino: Id del Item candidato a destino"""
    rela_ori = db_session.query(Relacion).filter_by(\
    fk_item_origen=item_origen, estado='ACT').all()

    for r_ori in rela_ori:
        if r_ori.fk_item_destino == item_destino:
            return True
        else:
            ori = r_ori.fk_item_destino
            des = item_destino
            resultado = verificar_ciclos(ori, des)
            return resultado

    return False

def insertar_relacion(id_usuario, id_origen, id_destino):
    """Crea una nueva relacion entre Items
    @param id_usuario: Id del usuario actual
    @param id_origen: Id del item origen
    @param id_destino: Id del item destino"""
    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    destino = db_session.query(Item).filter_by(id=\
    id_destino).one()

    fecha_actual = datetime.now()
    usuario_modificador = usuario.id
    try:
        destino.estado = 'INA'
        destino.fecha_modificacion = fecha_actual
        destino.usuario_modificacion = usuario_modificador

        db_session.add(destino)
        db_session.flush()

        item_nuevo = Item()

        item_nuevo.nombre = destino.nombre
        item_nuevo.descripcion = destino.descripcion
        item_nuevo.observacion = destino.observacion
        item_nuevo.estado = 'ACT'
        item_nuevo.version = destino.version + 1
        item_nuevo.complejidad = destino.complejidad
        item_nuevo.costo = destino.costo
        item_nuevo.fk_tipo_item = destino.fk_tipo_item
        item_nuevo.item_anterior = destino.id
        item_nuevo.fecha_creacion = destino.fecha_creacion
        item_nuevo.usuario_creacion = destino.usuario_creacion
        item_nuevo.fecha_modificacion = fecha_actual
        item_nuevo.usuario_modificacion = usuario_modificador

        db_session.add(item_nuevo)
        db_session.flush()

        for valor in destino.valores:
            valor_nuevo = Valor()

            valor_nuevo.fk_item = item_nuevo.id
            valor_nuevo.fk_atributo = valor.fk_atributo
            valor_nuevo.valor_numerico = valor.valor_numerico
            valor_nuevo.valor_cadena = valor.valor_cadena
            valor_nuevo.valor_fecha = valor.valor_fecha
            valor_nuevo.valor_binario = valor.valor_binario

            db_session.add(valor_nuevo)
            db_session.flush()

        #Se guardan en el historial las relaciones del item en su 
        #version anterior, y se copian a la version actual
        relaciones_anteriores = db_session.query(Relacion).filter_by(\
        estado='ACT').filter(or_(\
        Relacion.fk_item_origen==destino.id, \
        Relacion.fk_item_destino==destino.id)).all()

        for rel_ant in relaciones_anteriores:
            copia_relacion = Relacion()

            if rel_ant.fk_item_origen == destino.id:
                copia_relacion.fk_item_origen = item_nuevo.id
                copia_relacion.fk_item_destino = rel_ant.fk_item_destino
            else:
                copia_relacion.fk_item_origen = rel_ant.fk_item_origen
                copia_relacion.fk_item_destino = item_nuevo.id

            copia_relacion.estado = rel_ant.estado

            rel_ant.estado = 'INA'

            db_session.add(rel_ant)
            db_session.add(copia_relacion)
            db_session.flush()

        relacion = Relacion()

        relacion.fk_item_origen = id_origen
        relacion.fk_item_destino = item_nuevo.id
        relacion.estado = 'ACT'

        db_session.add(relacion)
        db_session.flush()

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def listar_items_eliminados(nombre_proyecto, nombre_fase):
    """Presenta la lista de items eliminados, de manera a ser 
    seleccionados para luego ser revividos
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual"""
    lista_items = []

    proyecto = db_session.query(Proyecto).filter_by\
    (nombre=nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(fk_proyecto=proyecto.id).\
    filter_by(nombre=nombre_fase).one()

    tipos_item = db_session.query(TipoItem).filter_by(fk_fase=fase.id).\
    all()
    for tipoitem in tipos_item:
        items = db_session.query(Item).filter_by(\
        fk_tipo_item=tipoitem.id).all()
        for item in items:
            #Se omiten los items con estado Inactivo
            if item.estado == 'ELI':
                elemento_item = dict(id=item.id, nombre=item.nombre,\
                descripcion=item.descripcion, observacion=item.observacion, \
                estado=item.estado, complejidad=item.complejidad, \
                costo=item.costo, tipo_item=tipoitem.nombre, \
                version=item.version)

                lista_items.append(elemento_item)

    lista_items = sorted(lista_items, key=lambda elemento: elemento['nombre'])
    return lista_items

def validaciones_revivir_item(seleccionados):
    """Valida que se haya seleccionado al menos un item
    @param seleccionados: Lista de items seleccionados"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar al menos un item"
        return error

    return error

def obtener_id_activo(id_inactivo):
    """Recupera el Id del Item actualmente en estado Activo, a partir 
    de un Id de una version Inactiva anterior"""
    item = db_session.query(Item).filter_by\
    (item_anterior=id_inactivo).first()

    if item != None:
        res = obtener_id_activo(item.id)
        return res

    return id_inactivo

def revivir_items(nombre_proyecto, nombre_fase, id_usuario, \
    seleccionados):
    """Establece nuevamente un item a su estado Activo
    @param nombre_proyecto: Nombre del proyecto actual
    @param nombre_fase: Nombre de la fase actual
    @param id_usuario: Id del usuario logueado
    @param seleccionados: Lista de items seleccionados"""
    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    fecha_actual = datetime.now()

    try:
        for identif in seleccionados:
            identificador = int(identif)

            item = db_session.query(Item).filter_by(id=\
            identificador).one()

            #Verifica que el nombre del item revivido no se encuentre
            #actualmente en uso por otro item. En ese caso, asigna el
            #sufijo REV al nombre del item Revivido
            proyecto = db_session.query(Proyecto).filter_by(nombre=\
            nombre_proyecto).one()

            fase = db_session.query(Fase).filter_by(nombre=\
            nombre_fase, fk_proyecto=proyecto.id).one()

            tipos_item = db_session.query(TipoItem).filter_by(fk_fase=\
            fase.id).all()

            nombre_nuevo = item.nombre
            for tipo in tipos_item:
                lis_items = db_session.query(Item).filter_by(fk_tipo_item=\
                tipo.id).all()
                for it in lis_items:
                    if it.nombre == item.nombre and it.estado != 'INA' \
                    and it.estado != 'ELI':
                        nombre_nuevo = nombre_nuevo + "REV"

            #El sistema crea una nueva version del Item
            item_nuevo = Item()

            item_nuevo.nombre = nombre_nuevo
            item_nuevo.descripcion = item.descripcion
            item_nuevo.observacion = item.observacion
            item_nuevo.estado = 'ACT'
            item_nuevo.version = item.version + 1
            item_nuevo.complejidad = item.complejidad
            item_nuevo.costo = item.costo
            item_nuevo.fk_tipo_item = item.fk_tipo_item
            item_nuevo.item_anterior = item.id
            item_nuevo.fecha_creacion = item.fecha_creacion
            item_nuevo.usuario_creacion = item.usuario_creacion
            item_nuevo.fecha_modificacion = fecha_actual
            item_nuevo.usuario_modificacion = usuario.id

            db_session.add(item_nuevo)
            db_session.flush()

            #El sistema crea una nueva version de los valores
            for valor in item.valores:
                valor_nuevo = Valor()

                valor_nuevo.fk_item = item_nuevo.id
                valor_nuevo.fk_atributo = valor.fk_atributo
                valor_nuevo.valor_numerico = valor.valor_numerico
                valor_nuevo.valor_cadena = valor.valor_cadena
                valor_nuevo.valor_fecha = valor.valor_fecha
                valor_nuevo.valor_binario = valor.valor_binario

                db_session.add(valor_nuevo)
                db_session.flush()

            #El sistema actualiza el estado de las relaciones del item 
            #revivido a Activa, siempre y cuando estas no formen 
            #ciclos y los item con los cuales esta relacionado aun 
            #existan
            relaciones = db_session.query(Relacion).filter_by(\
            fk_item_origen=item.id).all()

            for relacion in relaciones:
                ori = item_nuevo.id
                id_activo = obtener_id_activo(relacion.fk_item_destino)
                des = id_activo
                hayCiclo = verificar_ciclos(des, ori)

                if not hayCiclo:
                    item_destino = db_session.query(Item).filter_by(\
                    id=id_activo).one()

                    if item_destino != 'ELI':
                        nueva_relacion = Relacion()

                        nueva_relacion.fk_item_origen = ori
                        nueva_relacion.fk_item_destino = des
                        nueva_relacion.estado = 'ACT'

                        db_session.add(nueva_relacion)
                        db_session.flush()

            relaciones = db_session.query(Relacion).filter_by(\
            fk_item_destino=item.id).all()

            for relacion in relaciones:
                id_activo = obtener_id_activo(relacion.fk_item_origen)
                ori = id_activo
                des = item_nuevo.id
                hayCiclo = verificar_ciclos(des, ori)

                if not hayCiclo:
                    item_origen = db_session.query(Item).filter_by(\
                    id=id_activo).one()

                    if item_origen != 'ELI':
                        nueva_relacion = Relacion()

                        nueva_relacion.fk_item_origen = ori
                        nueva_relacion.fk_item_destino = des
                        nueva_relacion.estado = 'ACT'

                        db_session.add(nueva_relacion)
                        db_session.flush()

            #El sistema establece el estado de los item sucesores e 
            #hijos en Revision
            resultado = establecer_estado_revision(item_nuevo.id)
            if resultado != 'OK':
                db_session.rollback()
                return resultado

            resultado = establecer_estado_aprobado()
            if resultado != 'OK':
                db_session.rollback()
                return resultado

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def verificar_parametros_activar_item(seleccionados):
    """Verifica que se haya seleccionado al menos un item, y que estos
    se encuentren en Revision o Aprobados
    @param seleccionados: Lista de items seleccionados"""
    error = None

    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar al menos un item"
        return error

    for elemento in seleccionados:
        id_item = int(elemento)

        item = db_session.query(Item).filter_by(id=id_item).one()

        if item.estado == 'ACT':
            error = "El item '" + item.nombre + "' ya se encuentra en \
            estado Activo"
            return error

        if item.estado != 'REV' and item.estado != 'APR':
            error = "El item '" + item.nombre + "' no se encuentra en \
            estado Revision ni Aprobado"
            return error

        lista_lb = item.lineabase
        for lb in lista_lb:
            if lb.estado != 'ELI':
                error = "El item '" + item.nombre + "' forma parte de una \
                linea base"
                return error

        id_usuario = session["id_usuario"]
        duenho = usuario_dueno(id_item)

        if duenho != None and duenho != id_usuario:
            error = "No esta autorizado a modificar el Item '" + \
            item.nombre + "'"
            return error

    return error

def activar_item(id_usuario, seleccionados):
    """Establece el estado de los items seleccionados en Activo
    @param id_usuario: Id del usuario logueado
    @param seleccionados: Lista de items seleccionados"""
    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    fecha_actual = datetime.now()

    try:
        for elemento in seleccionados:
            id_item = int(elemento)

            item = db_session.query(Item).filter_by(id=id_item).one()
            item.estado = 'ACT'
            item.fecha_modificacion = fecha_actual
            item.usuario_modificacion = usuario.id

            db_session.add(item)
            db_session.flush()

            resultado = establecer_estado_revision(item.id)
            if resultado != 'OK':
                db_session.rollback()
                return resultado

            resultado = establecer_estado_aprobado()
            if resultado != 'OK':
                db_session.rollback()
                return resultado

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def establecer_finalizado(seleccionados):
    """Establece el cambio de estado de uno o mas items
    @param seleccionados: Lista de items seleccionados"""
    try:
        for elemento in seleccionados:
            id_item = int(elemento)
            item = db_session.query(Item).filter_by(id=\
            id_item).one()
            item.estado = "FIN"

            db_session.add(item)
            db_session.flush()

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def validacion_estado_finalizado(seleccionados):
    """Verifica que se seleccione al menos un item, y que este se 
    encuentre en estado Activo
    @param seleccionados: Lista de items seleccionados
    @return error: mensaje de error en caso de no cumplir con las validaciones"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar al menos un item"
        return error

    for elemento in seleccionados:
        id_item = int(elemento)
        item = db_session.query(Item).filter_by(id=\
        id_item).one()

        if item.estado == "FIN":
            error = "El item '" + item.nombre + "' ya se encuentra \
            Finalizado"
            return error

        if item.estado != "ACT":
            error = "El item '" + item.nombre + "' no se encuentra \
            en estado Activo"
            return error

        id_usuario = session["id_usuario"]
        duenho = usuario_dueno(id_item)

        if duenho != None and duenho != id_usuario:
            error = "No esta autorizado a modificar el Item '" + \
            item.nombre + "'"
            return error

    return error

def validacion_aprobar_item(seleccionados, nombre_fase, nombre_proyecto):
    """Verifica que se seleccione al menos un item, y que el item 
    seleccionado se encentre en estado de revision, ademas que el 
    estado del item padre sea aprobado o bloqueado; y que el item 
    antecesor sea bloqueado
    @param seleccionados: Lista de items seleccionados
    @return error: mensaje de error en caso de no cumplir con las 
                   validaciones"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar al menos un item"
        return error

    proyecto = db_session.query(Proyecto).filter_by\
    (nombre=nombre_proyecto).one()

    fase = db_session.query(Fase).filter_by(fk_proyecto=proyecto.id).\
    filter_by(nombre=nombre_fase).one()

    for elemento in seleccionados:
        id_item = int(elemento)
        item = db_session.query(Item).filter_by(id=\
        id_item).one()

        if item.estado == "APR":
            error = "El item '" + item.nombre + "' ya se encuentra \
            Aprobado"
            return error

        if item.estado != "FIN" and item.estado != "REV":
            error = "El item '" + item.nombre + "' no se encuentra en \
            estado Finalizado"
            return error

        #Verifica que si el tipo de item posee algun atributo binario
        #requerido, el mismo exista en el item
        tipo_item = db_session.query(TipoItem).filter_by\
        (id=item.fk_tipo_item).one()

        binarios_requeridos = db_session.query(Atributo).filter_by\
        (fk_tipo_item=tipo_item.id, tipo=4, obligatorio="S").all()

        for binario in binarios_requeridos:
            valor = db_session.query(Valor).filter_by\
            (fk_item=item.id, fk_atributo=binario.id).first()

            if valor == None:
                error = "El item '" + item.nombre + "' no posee todos \
                sus atributos binarios requeridos"
                return error

        relaciones = db_session.query(Relacion).filter_by\
        (fk_item_destino=id_item, estado='ACT').all()

        if fase.orden !=1 and len(relaciones) == 0:
            error = "El item '" + item.nombre + "' no posee un padre o \
            un antecesor"
            return error

        for relacion in relaciones:
            item_origen = db_session.query(Item).filter_by\
            (id=relacion.fk_item_origen).one()
            tipo_item = db_session.query(TipoItem).filter_by\
            (id=item_origen.fk_tipo_item).one()
            if fase.id == tipo_item.fk_fase:
                if item_origen.estado != "BLO" and \
                item_origen.estado != "APR" and str(item_origen.id)\
                not in seleccionados:
                    error = "El item Padre '" + item_origen.nombre + "' no se encuentra Aprobado"
                    return error
            else:
                if item_origen.estado != "BLO":
                    error = "El item Antecesor '" + item_origen.nombre + "' no se encuentra Bloqueado"
                    return error

    return error

def aprobar_item(seleccionados):
    """Aprueba el o lositems seleccionados
    @param seleccionados: Lista de items seleccionados"""
    try:
        for elemento in seleccionados:
            id_item = int(elemento)
            item = db_session.query(Item).filter_by(id=\
            id_item).one()

            item.estado = "APR"
            db_session.add(item)
            db_session.flush()

            #Si el item Aprobado fuese miembro de una linea base 
            #Comprometida, y ya no queda ningun otro item por 
            #aprobar, el sistema establece el estado de todos los item 
            #miembros en Bloqueado, y la linea base en Activa
            linea_base = item.lineabase

            for lb in linea_base:
                if lb.estado != 'ELI':
                    activar_linea_base = True
                    items = lb.items
                    for it in items:
                        if it.estado == 'REV':
                            activar_linea_base = False
                            break

                    if activar_linea_base:
                        lb.estado = 'ACT'
                        db_session.add(lb)
                        db_session.flush()

                        for it in items:
                            it.estado = 'BLO'
                            db_session.add(it)
                            db_session.flush()

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return 'OK'

def verificar_seleccionado_eliminar_item(seleccionados):
    """Verifica que se haya seleccionado un solo item para eliminar
    @param seleccionados: Lista de item seleccionados
    @return error: Retorna OK si se realizo la operacion correctamente,
                    de lo contrario retorna el mensaje de error"""
    error = "OK"
    if len(seleccionados) == 0:
        error = "Debe seleccionar un item"
        return error

    if len(seleccionados) > 1:
        error = "Debe seleccionar un unico item"
        return error

    id_item = int(seleccionados[0])
    item = db_session.query(Item).filter_by(id = id_item).one()
    
    if item.estado != "ACT":
        error = "El item debe estar Activo"

    id_usuario = session["id_usuario"]
    duenho = usuario_dueno(id_item)

    if duenho != None and duenho != id_usuario:
        error = "No esta autorizado a eliminar el Item"
        return error

    return error
    
def eliminacion_item(id_item_numerico):
    """Cambia el estado del item a Eliminado, establece sus relaciones a
    Inactiva, y setea sus hijos y sucesores a un estado de Revision
    @param id_item_numerico: Identificador del item
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    try:
        error = "OK"
        relaciones = db_session.query(Relacion).filter(\
        or_(Relacion.fk_item_origen==id_item_numerico,\
        Relacion.fk_item_destino==id_item_numerico)).all()
        
        for relacion in relaciones:
            relacion.estado = "INA"
            db_session.flush()
            
        result = establecer_estado_revision(id_item_numerico)
        if result != "OK":
            error = result
            db_session.rollback()
            return error
            
        result = establecer_estado_aprobado()
        if result != 'OK':
            error = result
            db_session.rollback()
            return error
        
        item = db_session.query(Item).filter_by(id=id_item_numerico).one()
        item.estado="ELI"
        
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    
    return error
    
def validar_seleccionados_eliminar_relacion(seleccionados):
    """Valida que se haya seleccionado un solo item
    @param seleccionados: Lista de items seleccionados
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    error = "OK"
    if len(seleccionados) == 0:
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1:
        error = "Debe seleccionar un unico item"
        return error
    
    id_item = int(seleccionados[0])
    item = db_session.query(Item).filter_by(id = id_item).one()
    
    if item.estado != "ACT":
        error = "El item debe estar Activo"

    return error
    
def obtener_relaciones_item(id_item):
    """Obtiene las relaciones con sus Item Origen y el Item Destino
    @param id_item: Identificador del item dentro de la BD
    @return lista: Lista de las relaciones del item"""
    lista = []
    item = db_session.query(Item).filter_by(id=id_item).one()
    tipoitem = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()
    fase = db_session.query(Fase).filter_by(id=tipoitem.fk_fase).one()
    
    relaciones = db_session.query(Relacion).filter(\
    or_(Relacion.fk_item_destino==id_item,\
    Relacion.fk_item_origen==id_item)).all()
    
    for relacion in relaciones:
        
        item_origen = db_session.query(Item).filter_by(\
        id=relacion.fk_item_origen).one()
        
        tipoitem_origen = db_session.query(TipoItem).filter_by(\
        id=item_origen.fk_tipo_item).one()
        
        fase_origen = db_session.query(Fase).filter_by(\
        id=tipoitem_origen.fk_fase).one()
        
        
        item_destino = db_session.query(Item).filter_by(\
        id=relacion.fk_item_destino).one()
        
        tipoitem_destino = db_session.query(TipoItem).filter_by(\
        id=item_destino.fk_tipo_item).one()
        
        fase_destino = db_session.query(Fase).filter_by(\
        id=tipoitem_destino.fk_fase).one()
        
        relacion = dict(id=relacion.id, origen= fase_origen.nombre + " -- " +\
        item_origen.nombre + ".v" + str(item_origen.version),\
        destino = fase_destino.nombre + " -- " + item_destino.nombre + ".v" +\
        str(item_destino.version))
        lista.append(relacion)
    
    return lista
    
def validar_eliminacion_relacion(seleccionados):
    """Valida que se haya seleccionado una sola relacion del item
    @param seleccionados: Lista de las relaciones del item
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    error = "OK"
    if len(seleccionados) == 0:
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1:
        error = "Debe seleccionar un unico item"
        return error

    return error    
    
def eliminacion_relacion(id_relacion, id_item):
    """Establece a inactiva la relacion y inserta en el historial la 
    version anterior del item
    @param id_relacion: Identificador de la relacion dentro de la BD
    @param id_item: item de la relacion
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    try:
        error = "OK"
        fecha_actual = datetime.now()
        
        id_usuario = session['id_usuario']
        usuario = db_session.query(Usuario).filter_by(\
        id_usuario=id_usuario).one()
        
        #busca la relacion a eliminar
        relacion_a_eliminar = db_session.query(Relacion).filter_by(\
        id=id_relacion).one()
        
        #busca el item de la relacion a eliminar
        item_actual = db_session.query(Item).filter_by(id=id_item).one()
        
        #se hace una copia del item de la relacion a eliminar y se 
        #aumenta el numero de version para el item nuevo
        item_siguiente = Item()
        item_siguiente.nombre = item_actual.nombre
        item_siguiente.descripcion = item_actual.descripcion
        item_siguiente.observacion = item_actual.observacion
        item_siguiente.estado = item_actual.estado
        item_siguiente.version = item_actual.version + 1
        item_siguiente.complejidad = item_actual.complejidad
        item_siguiente.costo = item_actual.costo
        item_siguiente.fk_tipo_item = item_actual.fk_tipo_item
        item_siguiente.item_anterior = item_actual.id
        item_siguiente.fecha_creacion = item_actual.fecha_creacion
        item_siguiente.usuario_creacion = item_actual.usuario_creacion
        item_siguiente.fecha_modificacion = fecha_actual
        item_siguiente.usuario_modificacion = usuario.id
        
        #se setea el item de la relacion a eliminar a Inactivo
        item_actual.estado = "INA"
        
        db_session.add(item_siguiente)
        db_session.flush()
        
        #se realiza una copia de los valores de los atributos del item 
        #de la relacion a eliminar
        for valor_actual in item_actual.valores:
            valor_siguiente = Valor()
            valor_siguiente.fk_item = item_siguiente.id
            valor_siguiente.fk_atributo = valor_actual.fk_atributo
            valor_siguiente.valor_numerico = valor_actual.valor_numerico
            valor_siguiente.valor_cadena = valor_actual.valor_cadena
            valor_siguiente.valor_fecha = valor_actual.valor_fecha
            valor_siguiente.valor_binario = valor_actual.valor_binario
            
            db_session.add(valor_siguiente)
            db_session.flush()

        #se busca las relaciones que tiene el item de la relacion
        relaciones_atuales = db_session.query(Relacion).filter(
        or_(Relacion.fk_item_destino==item_actual.id,\
        Relacion.fk_item_origen==item_actual.id)).all()
        
        id_relacion_a_eliminar = relacion_a_eliminar.fk_item_origen
        
        #se realiza una copia de las relaciones del item de la relacion 
        #a eliminar
        for relacion_actual in relaciones_atuales:
            id_relacion_actual = relacion_actual.fk_item_origen
            #se establece a Inactiva todas las relaciones del item de 
            #la relacion a eliminar
            relacion_actual.estado = "INA"
            
            #se realiza una copia de todas las relaciones menos la 
            #que se quiere eliminar
            if id_relacion_a_eliminar != id_relacion_actual:
                
                if relacion_actual.fk_item_origen == item_actual.id:
                    relacion_siguiente = Relacion()
                    relacion_siguiente.fk_item_origen  = item_siguiente.id
                    relacion_siguiente.fk_item_destino = relacion_actual.fk_item_destino
                    relacion_siguiente.estado = "ACT"
                else:
                    relacion_siguiente = Relacion()
                    relacion_siguiente.fk_item_origen  = relacion_actual.fk_item_origen
                    relacion_siguiente.fk_item_destino = item_siguiente.id
                    relacion_siguiente.estado = "ACT"

                db_session.add(relacion_siguiente)
                db_session.flush()
            
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    
    return error

def validacion_seleccionados_revertir_item(seleccionados):
    """Valida que se haya seleccionado un solo item para revertir sus 
    versiones
    @param seleccionados: Lista de items seleccionados
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    error = "OK"
    if len(seleccionados) == 0:
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1:
        error = "Debe seleccionar un unico item"
        return error
    
    id_item = seleccionados[0]
    id_item_numerico = int(id_item)
    item = db_session.query(Item).filter_by(id=id_item_numerico).one()
    if item.estado != "ACT":
        error = "El item debe estar Activo"

    return error
    
    
def obtener_versiones_items(id_item):
    """Retorna una lista (id, versiones) del item
    @param  id_item: Identificador del item dentro de la BD
    @return lista: Lista de las versiones anteriores del item"""
    lista = []
    sw = False
    
    item = db_session.query(Item).filter_by(id=id_item).one()
    anterior = item.item_anterior;
    
    elemento = dict(id=item.id, version=item.version)
    lista.append(elemento)
        
    while True:
        if anterior == None:
            break
            
        item = db_session.query(Item).filter_by(id=anterior).one()
        elemento = dict(id=item.id, version=item.version)
        lista.append(elemento)
        anterior = item.item_anterior
        
    return lista

def validar_revertir_item(id_item_actual, id_item):
    """Valida que la version del item selecionado no sea igual al item 
    a revertir
    @param id_item_actual: Identificador de la version el item seleccionado
    @param id_item: Item a revertir
    return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    if id_item_actual==id_item:
        error = "Debe seleccionar una version anterior"
        return error
    
    return "OK"
    
def revercion_item(id_item_anterior, id_item_actual):
    """Establece a Inactivo al item y hace una copia de la version del 
    item seleccionado y la establece a Activa
    @param id_item_actual: Version anterior selecionada 
    @param id_item: Item con la version actual
    @return error: Retorna OK si se realizo la operacion correctamente,
    de lo contrario retorna el mensaje de error"""
    try:
        fecha_actual = datetime.now()
            
        id_usuario = session['id_usuario']
        usuario = db_session.query(Usuario).filter_by(\
        id_usuario=id_usuario).one()
        #obtenemos el item de una de las versiones anterior
        item_anterior = db_session.query(Item).filter_by(\
        id=id_item_anterior).one()
        #obtenemos el item de la version actual
        item_actual = db_session.query(Item).filter_by(\
        id=id_item_actual).one()
        
        nombre_proyecto = session["nombre_proyecto"]
        nombre_fase = session["nombre_fase"]
        proyecto = db_session.query(Proyecto).filter_by(\
        nombre=nombre_proyecto).one()
        fase = db_session.query(Fase).filter_by(\
        nombre=nombre_fase).one()
        tipos_item = db_session.query(TipoItem).filter_by(\
        fk_fase=fase.id).all()
        #obtenemos el nombre de la version del item que queremos revertir
        nombre_item = item_anterior.nombre
        contador = 1
        #verificamos que el item revertido tenga nombre diferente a los items existentes
        for tipo_item in tipos_item:
            #obtenemos los items asociados al tipo de item Activos, Finalizados, 
            #Aprobados, Revision, Bloqueados
            items = db_session.query(Item).filter(\
            Item.fk_tipo_item==tipo_item.id).filter(Item.estado!="INA").\
            filter(Item.estado!="ELI").all()
            for item in items:
                #verificamos si existe un item en los estados citados anteriormente
                #que tenga el mismo nombre del item que queremos revivir
                if item.nombre == item_anterior.nombre and item.id != item_actual.id:
                    #en caso de que exista un item con el mismo nombre agregamos "REV" 
                    #mas un numero al final del nombre del item que queremos revertir
                    patron = item_anterior.nombre.find("REV")
                    if patron == -1:
                        nombre_item = item_anterior.nombre + "REV" + str(contador)
                    else:
                        nombre_item = item_anterior.nombre + str(contador)
                    contador = contador + 1
                    
        #replicamos los valores del item a revertir
        item_siguiente = Item()
        item_siguiente.nombre = nombre_item
        item_siguiente.descripcion = item_anterior.descripcion
        item_siguiente.observacion = item_anterior.observacion
        item_siguiente.estado = "ACT"
        item_siguiente.version = item_actual.version + 1
        item_siguiente.complejidad = item_anterior.complejidad
        item_siguiente.costo = item_anterior.costo
        item_siguiente.fk_tipo_item = item_anterior.fk_tipo_item
        item_siguiente.item_anterior = item_actual.id
        item_siguiente.fecha_creacion = item_anterior.fecha_creacion
        item_siguiente.usuario_creacion = item_anterior.usuario_creacion
        item_siguiente.fecha_modificacion = fecha_actual
        item_siguiente.usuario_modificacion = usuario.id
        db_session.add(item_siguiente)
        
        #establecemos el item actual a Inactivo
        item_actual.estado = "INA"
        db_session.flush()
        
        #replicamos los valores de los atributos del item a revertir
        for valor_anterior in item_anterior.valores:
            valor_siguiente = Valor()
            valor_siguiente.fk_item = item_siguiente.id
            valor_siguiente.fk_atributo = valor_anterior.fk_atributo
            valor_siguiente.valor_numerico = valor_anterior.valor_numerico
            valor_siguiente.valor_cadena = valor_anterior.valor_cadena
            valor_siguiente.valor_fecha = valor_anterior.valor_fecha
            valor_siguiente.valor_binario = valor_anterior.valor_binario
            db_session.add(valor_siguiente)
            db_session.flush()
            
        #obtenemos las relaciones del item a revertir
        relaciones_anteriores = db_session.query(Relacion).filter(\
        or_(Relacion.fk_item_destino==item_anterior.id, \
        Relacion.fk_item_origen==item_anterior.id)).all()
        
        #obtenemos las relaciones del item con la version actual
        relaciones_actuales = db_session.query(Relacion).filter(\
        or_(Relacion.fk_item_destino==item_actual.id, \
        Relacion.fk_item_origen==item_actual.id)).all()
        
        #replicamos las relaciones del item a revertir
        for relacion_anterior in relaciones_anteriores:
            
            #obtenemos el item al que apunta la relacion origen
            item_relacion_origen = db_session.query(Item).filter_by(\
            id=relacion_anterior.fk_item_origen).one()
            
            #obtenemos el item al que apunta la relacion destino
            item_relacion_destino = db_session.query(Item).filter_by(\
            id=relacion_anterior.fk_item_destino).one()
                
            if item_relacion_origen.id == item_anterior.id:
                #obtiene el id del la ultima version del item destino de la relacion
                id_destino_activo = obtener_id_activo(item_relacion_destino.id)
                
                #verifica si hay un ciclo entre la ultima version del item destino
                #y el la copia del item a revertir
                hayCiclo = verificar_ciclos(id_destino_activo, item_siguiente.id)
                
                if not hayCiclo:
                    #si no hay ciclo obtenemos la ultima version del item
                    #destino de la relacion del item a revertir
                    item_destino_activo = db_session.query(Item).filter_by(\
                    id=id_destino_activo).one()
                    
                    #si el item destino de la relacion no esta eliminado
                    #replicamos la relacion
                    if item_destino_activo != 'ELI':
                        relacion_siguiente = Relacion()
                        relacion_siguiente.fk_item_origen = item_siguiente.id
                        relacion_siguiente.fk_item_destino = item_destino_activo.id
                        relacion_siguiente.estado = "ACT"
                        db_session.add(relacion_siguiente)
                        db_session.flush() 
                        
            else:
                #obtiene el id del la ultima version del item origen de la relacion                
                id_origen_activo = obtener_id_activo(item_relacion_origen.id)
                
                #verifica si hay un ciclo entre la ultima version del item origen
                #y el la copia del item a revertir
                hayCiclo = verificar_ciclos(item_siguiente.id, id_origen_activo)
                
                if not hayCiclo:
                    #si no hay ciclo obtenemos la ultima version del item
                    #origen de la relacion del item a revertir
                    item_origen_activo = db_session.query(Item).filter_by(\
                    id=id_origen_activo).one()

                    #si el item origen de la relacion no esta eliminado
                    #replicamos la relacion
                    if item_origen_activo != 'ELI':
                        relacion_siguiente = Relacion()
                        relacion_siguiente.fk_item_origen = item_origen_activo.id
                        relacion_siguiente.fk_item_destino = item_siguiente.id
                        relacion_siguiente.estado = "ACT"
                        db_session.add(relacion_siguiente)
                        db_session.flush()
        
        #establecemos a Inactiva las relaciones de la version actual del item
        for relacion_actual in relaciones_actuales:
            relacion_actual.estado = "INA"
            db_session.flush()
        
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    
    return "OK"
    
def obtener_relaciones_activas_item(id_item):
    """Obtiene las relaciones activas con sus Item Origen y el Item Destino
    @param id_item: Identificador del item dentro de la BD
    @return lista: Lista de las relaciones activas del item"""
    lista = []
    item = db_session.query(Item).filter_by(id=id_item).one()
    tipoitem = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()
    fase = db_session.query(Fase).filter_by(id=tipoitem.fk_fase).one()
    
    relaciones = db_session.query(Relacion).filter(\
    or_(Relacion.fk_item_destino==id_item,\
    Relacion.fk_item_origen==id_item)).filter_by(estado="ACT").all()
    
    for relacion in relaciones:
        
        item_origen = db_session.query(Item).filter_by(\
        id=relacion.fk_item_origen).one()
        
        tipoitem_origen = db_session.query(TipoItem).filter_by(\
        id=item_origen.fk_tipo_item).one()
        
        fase_origen = db_session.query(Fase).filter_by(\
        id=tipoitem_origen.fk_fase).one()
        
        
        item_destino = db_session.query(Item).filter_by(\
        id=relacion.fk_item_destino).one()
        
        tipoitem_destino = db_session.query(TipoItem).filter_by(\
        id=item_destino.fk_tipo_item).one()
        
        fase_destino = db_session.query(Fase).filter_by(\
        id=tipoitem_destino.fk_fase).one()
        
        relacion = dict(id=relacion.id, origen= fase_origen.nombre + " -- " + \
        item_origen.nombre, destino = fase_destino.nombre + " -- " + item_destino.nombre)
        lista.append(relacion)
    
    return lista

def verificar_parametros_ver_relaciones(seleccionados):
    """Verifica que se seleccione un item, y solo uno"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un item
        error = "Debe seleccionar un unico item"
        return error

    return error

def listar_relaciones(id_item):
    """Devuelve la lista de relaciones del Item actual"""
    items=[]

    item = db_session.query(Item).filter_by(id=id_item).one()

    item_tipo = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()

    item_fase = db_session.query(Fase).filter_by(\
    id=item_tipo.fk_fase).one()

    item_nombre = item_fase.nombre + "-" + item.nombre

    relaciones = db_session.query(Relacion).filter_by(\
    fk_item_origen=id_item, estado="ACT").all()

    for relacion in relaciones:
        destino = db_session.query(Item).filter_by(\
        id=relacion.fk_item_destino).one()

        tipo = db_session.query(TipoItem).filter_by(\
        id=destino.fk_tipo_item).one()

        fase = db_session.query(Fase).filter_by(\
        id=tipo.fk_fase).one()

        nombre = fase.nombre + "-" + destino.nombre

        elemento = dict(id=relacion.id, origen=item_nombre, 
        destino=nombre)
        items.append(elemento)

    relaciones = db_session.query(Relacion).filter_by(\
    fk_item_destino=id_item, estado="ACT").all()

    for relacion in relaciones:
        origen = db_session.query(Item).filter_by(\
        id=relacion.fk_item_origen).one()

        tipo = db_session.query(TipoItem).filter_by(\
        id=origen.fk_tipo_item).one()

        fase = db_session.query(Fase).filter_by(\
        id=tipo.fk_fase).one()

        nombre = fase.nombre + "-" + origen.nombre

        elemento = dict(id=relacion.id, origen=nombre, 
        destino=item_nombre)
        items.append(elemento)

    return items

def verificar_parametros_boton_subirarchivo(seleccionados):
    """Verifica que se seleccione un item y solo uno, 
    ademas de que el item a modificar se encuentre Activo y que 
    efectivamente tenga algun atributo binario
    @param seleccionados: Lista de items seleccionados"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un item
        error = "Debe seleccionar un unico item"
        return error

    id_item = int(seleccionados[0])
    item = db_session.query(Item).filter_by(id=id_item).one()

    #El item a modificar debe estar en estado Activo
    if item.estado == 'REV':
        error = "Se requiere un cambio de estado a Activo"
        return error

    if item.estado == 'APR':
        error = "Se requiere un cambio de estado a Activo"
        return error

    if item.estado == 'BLO':
        error = "No se puede modificar un item Bloqueado"
        return error

    tipo_item = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()

    continuar = False
    for atributo in tipo_item.atributos:
        if atributo.tipo == 4:
            continuar = True
            break

    if not continuar:
        error = "El item '" + item.nombre + "' no posee atributos de \
        tipo binario"
        return error

    id_usuario = session["id_usuario"]
    duenho = usuario_dueno(id_item)

    if duenho != None and duenho != id_usuario:
        error = "No esta autorizado a modificar el Item"
        return error

    return error

def verificar_parametros_boton_descargararchivo(seleccionados):
    """Verifica que se seleccione un item y solo uno, 
    ademas de que el item efectivamente tenga algun atributo binario
    @param seleccionados: Lista de items seleccionados"""
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un item
        error = "Debe seleccionar un unico item"
        return error

    id_item = int(seleccionados[0])
    item = db_session.query(Item).filter_by(id=id_item).one()

    tipo_item = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()

    continuar = False
    for atributo in tipo_item.atributos:
        if atributo.tipo == 4:
            continuar = True
            break

    if not continuar:
        error = "El item '" + item.nombre + "' no posee atributos de \
        tipo binario"
        return error

    return error

def listar_atributos_archivo(id_item):
    """Devuelve la lista de atributos binarios del item seleccionado
    @param id_item: Identificador del item seleccionado"""
    lista = []

    item = db_session.query(Item).filter_by(id=id_item).one()

    tipo_item = db_session.query(TipoItem).filter_by(\
    id=item.fk_tipo_item).one()

    for atributo in tipo_item.atributos:
        if atributo.tipo == 4:
            elemento = dict(id=atributo.id, nombre=atributo.nombre)
            lista.append(elemento)

    lista = sorted(lista, key=lambda elemento: elemento['nombre'])

    return lista

def guardar_archivo(id_item, id_atributo, archivo, id_usuario):
    """Guarda en la base de datos el archivo seleccionado
    @param id_item: Identificador del item seleccionado
    @param id_atributo: Identificador del atibuto seleccionado
    @param archivo: El archivo a almacenar
    @param id_usuario: Usuario actual"""
    try:
        #Se registra la modificacion del Item
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()

        fecha_actual = datetime.now()
        usuario_modificador = usuario.id

        item = db_session.query(Item).filter_by(id=\
        id_item).one()

        item.estado = 'INA'
        item.fecha_modificacion = fecha_actual
        item.usuario_modificacion = usuario_modificador

        db_session.add(item)
        db_session.flush()

        item_nuevo = Item()

        item_nuevo.nombre = item.nombre
        item_nuevo.descripcion = item.descripcion
        item_nuevo.observacion = item.observacion
        item_nuevo.estado = 'ACT'
        item_nuevo.version = item.version + 1
        item_nuevo.complejidad = item.complejidad
        item_nuevo.costo = item.costo
        item_nuevo.fk_tipo_item = item.fk_tipo_item
        item_nuevo.item_anterior = item.id
        item_nuevo.fecha_creacion = fecha_actual
        item_nuevo.usuario_creacion = usuario_modificador
        item_nuevo.fecha_modificacion = fecha_actual
        item_nuevo.usuario_modificacion = usuario_modificador

        db_session.add(item_nuevo)
        db_session.flush()

        for valor in item.valores:
            valor_nuevo = Valor()

            valor_nuevo.fk_item = item_nuevo.id
            valor_nuevo.fk_atributo = valor.fk_atributo
            valor_nuevo.valor_numerico = valor.valor_numerico
            valor_nuevo.valor_cadena = valor.valor_cadena
            valor_nuevo.valor_fecha = valor.valor_fecha
            valor_nuevo.valor_binario = valor.valor_binario

            db_session.add(valor_nuevo)
            db_session.flush()

        #Se registra el archivo nuevo
        valor = db_session.query(Valor).filter_by(\
        fk_item=item_nuevo.id, fk_atributo=id_atributo).first()

        if valor == None:
            valor = Valor()
            valor.fk_item = item_nuevo.id
            valor.fk_atributo = id_atributo

        valor.valor_binario = archivo.getvalue()
        db_session.add(valor)
        db_session.flush()

        #Se registra la modificacion en las relaciones
        relaciones = db_session.query(Relacion).filter_by(\
        fk_item_destino=item.id, estado='ACT').all()

        for relacion in relaciones:
            relacion_nueva = Relacion()

            relacion_nueva.fk_item_origen = relacion.fk_item_origen
            relacion_nueva.fk_item_destino = item_nuevo.id
            relacion_nueva.estado = 'ACT'

            db_session.add(relacion_nueva)
            db_session.flush()

            relacion.estado = 'INA'

            db_session.add(relacion)
            db_session.flush()

        relaciones = db_session.query(Relacion).filter_by(\
        fk_item_origen=item.id, estado='ACT').all()

        for relacion in relaciones:
            relacion_nueva = Relacion()

            relacion_nueva.fk_item_origen = item_nuevo.id
            relacion_nueva.fk_item_destino = relacion.fk_item_destino
            relacion_nueva.estado = 'ACT'

            db_session.add(relacion_nueva)
            db_session.flush()

            relacion.estado = 'INA'

            db_session.add(relacion)
            db_session.flush()

        resultado = establecer_estado_revision(item_nuevo.id)
        if resultado != 'OK':
            db_session.rollback()
            return resultado

        resultado = establecer_estado_aprobado()
        if resultado != 'OK':
            db_session.rollback()
            return resultado

        #Se actualiza el ID del item actual
        session['id_item'] = item_nuevo.id

        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return "OK"

def obtener_archivo(id_item, id_atributo):
    """Retorna el archivo almacenado en el item seleccionado
    @param id_item: Identificador del item seleccionado
    @param id_atributo: Identificador del atributo seleccionado"""
    valor = db_session.query(Valor).filter_by(\
    fk_item=id_item, fk_atributo=id_atributo).first()

    if valor != None:
        archivo = valor.valor_binario
        return archivo

    return None

def generar_nombre_archivo(id_item, id_atributo):
    """Genera el nombre para el archivo a recuperar de la base de datos
    @param id_item: Identificador del item seleccionado
    @param id_atributo: Identificador del atributo seleccionado"""
    try:
        item = db_session.query(Item).filter_by(\
        id=id_item).one()

        atributo = db_session.query(Atributo).filter_by(\
        id=id_atributo).one()

        nombre = item.nombre + "-" + atributo.nombre
        return nombre

    except exc.SQLAlchemyError, e:
        return "Archivo"

def verificar_boton_agregar_relacion(id_item):
    """Verifica que el item no se encuentre Aprobado o Bloqueado"""
    error = None

    item = db_session.query(Item).filter_by(\
    id=id_item).one()

    if item.estado == "APR":
        error = "El item se encuentra Aprobado"
        return error

    if item.estado == "BLO":
        error = "El item se encuentra Bloqueado"
        return error

    for lb in item.lineabase:
        if lb.estado != 'ELI':
            error = "El item forma parte de una linea base"
            return error

    id_usuario = session["id_usuario"]
    duenho = usuario_dueno(id_item)

    if duenho != None and duenho != id_usuario:
        error = "No esta autorizado a modificar el Item"
        return error

    return error

def verificar_boton_reporte_historial(seleccionados):
    """Verifica que se haya seleccionado un solo item para la generacion
    del reporte
    @param seleccionados: Lista de items seleccionados"""
    error = None
    if len(seleccionados) == 0:
        error = "Debe seleccionar un item"
        return error

    if len(seleccionados) > 1:
        error = "Debe seleccionar un unico item"
        return error

    return error

def usuario_dueno(id_item):
    """Retorna el id_usuario del duenho del item seleccionado, 
    si tuviera uno. En caso contrario retorna None
    @param id_item: Identificador del item seleccionado"""
    respuesta = None

    item = db_session.query(Item).filter_by(id=id_item).one()
    continuar = True

    while continuar:
        lista_solicitudes = item.solicitud

        mayor_id = 0
        for solicitud in lista_solicitudes:
            if solicitud.estado == 'APR' and solicitud.id > mayor_id:
                usuario = db_session.query(Usuario).filter_by(\
                id=solicitud.usuario_creacion).one()

                respuesta = usuario.id_usuario
                mayor_id = solicitud.id

        if item.version > 1:
            id_item_anterior = item.item_anterior

            item = db_session.query(Item).filter_by(\
            id=id_item_anterior).one()
        else:
            continuar = False

    return respuesta
    
def verificar_parametros_crear_grafo_item(seleccionados):
    error = None
    if len(seleccionados) == 0: #No selecciono ningun item
        error = "Debe seleccionar un item"
        return error
    if len(seleccionados) > 1: #Selecciono mas de un item
        error = "Debe seleccionar un unico item"
        return error
    
    return error

def obtener_nombre_fase_item(id_item):
    
    item = db_session.query(Item).filter_by(id = id_item).one()
    tipo_item = db_session.query(TipoItem).filter_by(id = item.fk_tipo_item).one()
    fase_item = db_session.query(Fase).filter_by(id = tipo_item.fk_fase).one()
    return fase_item.nombre
